﻿#ifndef ZGLOBALSIGNAL_H
#define ZGLOBALSIGNAL_H

/*******************************************************************************
** 全局信号传递机制实现类。
**
*******************************************************************************/

#include "ZFCommonHeader.h"
#include <QObject>
#include <QMap>
#include <QMutex>
#include <QPointer>

ZFLIB_BEGIN_NAMESPACE

class ZFLIB_API ZGlobalSignal : public QObject
{
    Q_OBJECT

public:
    static ZGlobalSignal *instance();
    ~ZGlobalSignal();

    /**
     * @brief doEmit
     *  注册全局信号。如果想取消注册全局信号，可以主动undoEmit，或者等待
     *  对象销毁自动取消。
     *  会自动去除重复的信号，不用担心重复调用的问题。
     * @param sender
     *  发送者
     * @param signal
     *  信号签名
     */
    void doEmit(QObject *sender, const char *signal);

    /**
     * @brief undoEmit
     *  取消注册全局信号
     * @param sender
     * @param signal
     */
    void undoEmit(QObject *sender, const char *signal);

    /**
     * @brief doSlot
     *  连接到信号并注册槽
     * @param signal
     *  信号签名
     * @param recver
     *  接收者指针
     * @param slot
     *  槽签名
     */
    void doSlot(const char *signal, QObject *recver, const char *slot);

    /**
     * @brief undoSlot
     *  取消响应全局信号
     * @param signal
     * @param recver
     * @param slot
     */
    void undoSlot(const char *signal, QObject *recver, const char *slot);

private slots:
    /**
     * @brief slotSenderDestoryed
     *  监听发送者销毁的信号，做清理工作
     * @param obj
     */
    void slotSenderDestoryed(QObject *sender);

    /**
     * @brief slotRecverDestoryed
     *  接收者被销毁信号响应，做清理工作
     * @param sender
     */
    void slotRecverDestoryed(QObject *recver);

private:
    explicit ZGlobalSignal(QObject *parent = NULL);

private:
    // 以接收者为主键，可以查询到的内容
    struct SlotInfo
    {
        QObject *recver; /* 接收者指针，使用观察者指针 */
        QString slot_name; /* 槽名称 */
        QString signal_name; /* 信号名称 */
    };

    // 以信号为主键，可以查询到的信息
    struct SignalInfo
    {
        QString signal_name; /* 信号名称 */
        QList<QObject *> sender_list; /* 发送者列表 */
        QList<SlotInfo *> slot_list; /* 槽列表 */
    };

    // 以发送者为主键，需要查询到的信息
    struct SenderInfo
    {
        QList<QString> signal_list;
    };

    struct RecverInfo
    {
        QList<QString> signal_list;
    };

    // 信号查询表
    // 使用QString代替const char *，防止字符串仅进行地址比较，导致查询失败
    QMap<QString, SignalInfo *> m_signal_view;

    // 发送者查询表
    QMap<QObject *,  SenderInfo *> m_sender_view;

    // 接收最后查询表
    QMap<QObject *, RecverInfo *> m_recver_view;

    // 互斥锁
    QMutex m_mutex;
};

ZFLIB_END_NAMESPACE

#endif // ZGLOBALSIGNAL_H
